跳到主要内容

Vitest + Playwright

pnpm add -D jsdom
pnpm add -D vitest
pnpm add -D vitest-browser-react
pnpm add -D @vitest/browser
pnpm add -D @vitest/browser-playwright

vite.config.ts:

/// <reference types="vitest/config" />
import { defineConfig } from 'vite'
import viteReact from '@vitejs/plugin-react'
import { playwright } from '@vitest/browser-playwright'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
import { resolve } from 'node:path'

// https://vitejs.dev/config/
export default defineConfig({
plugins: [
tanstackRouter({
target: 'react',
autoCodeSplitting: true
}),
viteReact(),
],
test: {
// 浏览器测试配置
browser: {
enabled: true,
provider: playwright(),
instances: [
{ browser: 'chromium' },
],
headless: false, // 设置为 false 以显示浏览器窗口
ui: true, // 启用 UI 模式,测试完成后不自动关闭浏览器
},
// 常规测试配置
environment: 'jsdom',
},
resolve: {
alias: {
'@': resolve(__dirname, './src'),
},
},
})

package.json:

  • test:运行常规测试和浏览器测试(仅打开浏览器测试,测试完成自动关闭)
  • test:ui: 运行测试并保持浏览器打开
{
"scripts": {
"test": "vitest run",
"test:ui": "vitest --browser --browser.ui",
},
}

src/HW.tsx

import { useState } from 'react'

export default function HelloWorld({ name }: { name: string }) {
const [count, setCount] = useState(1)
return (
<div>
<h1>
Hello {name} x{count}!
</h1>
<button onClick={() => setCount((c) => c + 1)}>Increment</button>
</div>
)
}

tests/HW.spec.tsx:

import { expect, test } from 'vitest'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import HelloWorld from '@/components/HelloWorld'

test('renders name', async () => {
render(<HelloWorld name="Vitest" />)

// 使用 Vitest 的原生断言
expect(screen.getByText('Hello Vitest x1!')).toBeDefined()

const user = userEvent.setup()
await user.click(screen.getByRole('button', { name: 'Increment' }))

expect(screen.getByText('Hello Vitest x2!')).toBeDefined()
})

test-browser/HW.spec.tsx:

import { expect, test, afterEach } from 'vitest'
import { render, screen, cleanup } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import HelloWorld from '@/components/HelloWorld'

// 在每个测试后清理 DOM
afterEach(() => {
cleanup()
})

test('renders name in browser', async () => {
// 在浏览器环境中渲染组件
render(<HelloWorld name="Playwright" />)

// 验证初始渲染
expect(screen.getByText('Hello Playwright x1!')).toBeDefined()

// 模拟用户交互
const user = userEvent.setup()
await user.click(screen.getByRole('button', { name: 'Increment' }))

// 验证交互后的状态
expect(screen.getByText('Hello Playwright x2!')).toBeDefined()
})

test('multiple increments work correctly', async () => {
render(<HelloWorld name="Browser" />)

const user = userEvent.setup()

// 点击按钮多次
await user.click(screen.getByRole('button', { name: 'Increment' }))
await user.click(screen.getByRole('button', { name: 'Increment' }))
await user.click(screen.getByRole('button', { name: 'Increment' }))

expect(screen.getByText('Hello Browser x4!')).toBeDefined()
})

官方在线演示demo: https://stackblitz.com/edit/vitest-tests-browser-examples-d8kteiex?file=vitest.config.ts&initialPath=__vitest__/